import { Repository, SelectQueryBuilder } from 'typeorm';
import { Class_HttpException_Base } from '../classes/httpException';

type IUtil_Db_AddAnyWhere_Arguements<T> = {
  queryBuilder: SelectQueryBuilder<T>;
  obj: Record<string, unknown>;
};
/**
 * @description 该函数接收一个查询构建器和一个条件对象。它将对象中的非空键值对转换为查询条件，并附加到查询构建器上。
 * @example
 * util_Db_AddAnyWhere({ queryBuilder: data.queryBuilder, obj });
 * @template T 数据实体类型。
 * @param {SelectQueryBuilder<T>} queryBuilder - TypeORM 的查询构建器实例，用于构建数据库查询。
 * @param {Record<string, unknown>} obj - 一个记录对象，包含应用到查询的条件键值对。键是字段名，值是字段值。
 * @returns {SelectQueryBuilder<T>} 修改后的查询构建器，新增了查询条件。
 */
export function util_Db_AddAnyWhere<T>({
  queryBuilder,
  obj,
}: IUtil_Db_AddAnyWhere_Arguements<T>): SelectQueryBuilder<T> {
  if (!obj) return queryBuilder;
  Object.keys(obj).forEach((key) => {
    if (obj[key]) {
      if (Array.isArray(obj[key])) {
        queryBuilder.andWhere(`${key} IN (:...${key})`, { [key]: obj[key] });
      } else {
        queryBuilder.andWhere(`${key} = :${key}`, {
          [key]: obj[key],
        });
      }
    }
  });
  return queryBuilder;
}

type IUtil_Db_PageList_ArguementsQuery = Record<string, any> & { page?: number; size?: number };
type IUtil_Db_PageList_Arguements<T> = {
  queryBuilder: SelectQueryBuilder<T>;
  query: IUtil_Db_PageList_ArguementsQuery;
  isRaw?: boolean;
  getQuery?: boolean;
};
/**
 * @description 用于执行条件查询，可选地分页返回原始数据。
 * @example
 * const { size, page } = query;
 return await util_Db_PageList<Example>({
      queryBuilder: this.example_Repository
        .createQueryBuilder('example')
        .leftJoin('example.exampled', 'exampled')
        .select([
          'example.id AS id',
          'exampled.id AS exampleId',
        ]),
      query: {
        size: query.size,
        page: query.page,
      },
      isRaw: true,
    });
 * @template T 数据实体类型
 * @param {IUtil_Db_PageList_Arguements} data - TypeORM 的查询选项。
 * @param {SelectQueryBuilder<T>} data.queryBuilder - TypeORM 的查询构建器实例。
 * @param {QueryOptions} data.query - 查询选项，包含过滤条件和分页参数。
 * @param {boolean} [data.isRaw=false] - 是否返回原始数据格式。默认为 false，返回实体对象；为 true 时返回数据库原始数据。
 * @param {boolean} [data.getQuery=false] - 是否返回原始数据格式。默认为 false，返回实体对象；为 true 时返回数据库原始数据。
 * @returns {Promise<{ list: T[]; totalElements: number }>}
 */
export async function util_Db_PageList<T>(
  data: IUtil_Db_PageList_Arguements<T>,
): Promise<{ list: T[]; totalElements: number }> {
  const obj = JSON.parse(JSON.stringify(data.query));
  delete obj.size;
  delete obj.page;
  data.queryBuilder = util_Db_AddAnyWhere({ queryBuilder: data.queryBuilder, obj });
  const countQueryBuilder = data.queryBuilder.clone();
  data.query.size = data.query?.size || 10;
  data.query.page = data.query.page ? (data.query.page - 1) * data.query.size : 0;
  data.queryBuilder = data.isRaw
    ? data.queryBuilder.limit(data.query.size)
    : data.queryBuilder.take(data.query?.size);
  data.queryBuilder = data.isRaw
    ? data.queryBuilder.offset(data.query.page)
    : data.queryBuilder.skip(data.query.page);
  if (data.getQuery) console.log(data.queryBuilder.getQuery());
  const [list, totalElements] = await Promise.all([
    data.isRaw ? data.queryBuilder.getRawMany() : data.queryBuilder.getMany(),
    countQueryBuilder.getCount(),
  ]);
  return { list, totalElements };
}

type IUtil_Db_NoPageList_Arguements<T> = {
  queryBuilder: SelectQueryBuilder<T>;
  query?: Record<string, any>;
  isRaw?: boolean;
};
/**
 * @description 获取不分页的数据库查询结果。
 * @example return await util_Db_NoPageList<example>({
      queryBuilder: this.exampleRepository.createQueryBuilder('example').orderBy('example.order', 'ASC'),
      query,
    });
 * @template T 查询结果的类型
 * @param {IUtil_Db_NoPageList_Arguements<T>} params - 查询参数对象
 * @param {SelectQueryBuilder<T>} params.queryBuilder - TypeORM 的查询构建器
 * @param {Partial<T>} [params.query] - 查询条件对象
 * @param {boolean} [params.isRaw ] - 是否获取原始查询结果
 * @returns {Promise<T[]>} 返回查询结果数组的 Promise 对象
 */
export async function util_Db_NoPageList<T>({
  queryBuilder,
  query,
  isRaw,
}: IUtil_Db_NoPageList_Arguements<T>): Promise<T[]> {
  queryBuilder = util_Db_AddAnyWhere({ queryBuilder, obj: query });
  return isRaw ? await queryBuilder.getRawMany() : await queryBuilder.getMany();
}

type IUtil_Db_Add_ArguementsOne = {
  createDto: any;
  repository: any;
};
type IUtil_Db_Add_ArguementsTwo = IUtil_Db_Add_ArguementsOne & {
  returnObj: boolean;
};
type IUtil_Db_Add_Arguements = IUtil_Db_Add_ArguementsOne & {
  returnObj?: boolean;
};
/**
 * @description 新增记录到数据库
 * @example
 * return await util_Db_Add<Example>({
 *     createDto: createExampleDto,
 *     repository: this.exampleRepository,
 *   });
 * @template T 数据实体类型，表示返回数组中的元素类型。
 * @param {IUtil_Db_Add_Arguements} data - TypeORM 的查询选项。
 * @param {Object} data.createDto - 新增参数，包含将要存入数据库的字段。
 * @param {Repository} data.repository - 使用的数据仓库，提供数据操作方法。
 * @param {boolean} [data.returnObj] - 返回值是否是对象。
 * @returns {Promise<'新增成功' | T>}
 */
export async function util_Db_Add({
  createDto,
  repository,
}: IUtil_Db_Add_ArguementsOne): Promise<'新增成功'>;
export async function util_Db_Add<T>({
  createDto,
  repository,
  returnObj,
}: IUtil_Db_Add_ArguementsTwo): Promise<T>;
export async function util_Db_Add<T>(obj: IUtil_Db_Add_Arguements): Promise<'新增成功' | T> {
  const addItem = obj.repository.create(obj.createDto);
  const returnData = (await obj.repository.save(addItem)) as T;
  if ('returnObj' in obj) {
    return returnData;
  }
  return '新增成功';
}

type IUtil_Db_Update_ArguementsOne<T> = {
  findOne: (obj: any) => Promise<any>;
  updateDto: any;
  repository: Repository<T>;
};
type IUtil_Db_Update_ArguementsTwo<T> = IUtil_Db_Update_ArguementsOne<T> & {
  isReturnData: boolean;
};
type IUtil_Db_Update_Arguements<T> = IUtil_Db_Update_ArguementsOne<T> & {
  isReturnData?: boolean;
};
/**
 * @description 根据指定条件查找记录,若查找不到就抛出错误，查找到了就更新这条记录。
 * @example
 * return await util_Db_Update<Example>({
 *   findOne: () => this.exampleRepository.findOne(updateExampleDto.id),
 *   updateDto: updateExampleDto,
 *   repository: this.exampleRepository,
 * });
 * @template T 数据实体类型，表示返回数组中的元素类型。
 * @param {IUtil_Db_Update_Arguements<T>} data - TypeORM 的查询选项。
 * @param {Function} data.findOne - 一个函数，用于根据提供的标识符找到需要更新的记录。
 * @param {Object} data.updateDto - 包含更新数据的对象。
 * @param {Repository} data.repository - 数据库仓库，提供数据查找、合并和保存的方法。
 * @param {boolean} [data.isReturnData] - 返回值是否是编辑成功。
 * @returns {Promise<'编辑成功' | T>}
 */
export async function util_Db_Update<T>({
  findOne,
  updateDto,
  repository,
  isReturnData,
}: IUtil_Db_Update_ArguementsTwo<T>): Promise<T>;
export async function util_Db_Update<T>({
  findOne,
  updateDto,
  repository,
}: IUtil_Db_Update_ArguementsOne<T>): Promise<'编辑成功'>;
export async function util_Db_Update<T>({
  findOne,
  updateDto,
  repository,
  isReturnData,
}: IUtil_Db_Update_Arguements<T>) {
  const item = await findOne(updateDto.id);
  if (!item) throw new Class_HttpException_Base('不存在该记录');
  const newRole = repository.merge(item, updateDto);
  const returnData = await repository.save(newRole);
  if (!isReturnData) {
    return '编辑成功';
  }
  return returnData;
}

type IUtil_Db_Delete_Arguements = {
  findOne: (obj: any) => Promise<any>;
  repository: Repository<any>;
  id: number;
};
/**
 * @description 根据提供的 ID 查找 如果查找不到就抛出错误 查找到了就删除对应记录。
 * @example
 * return await util_Db_Delete({
 *   findOne: this.exampleRepository.findOne(id),
 *   id: 1,
 *   repository: this.exampleRepository,
 * });
 * @param {IUtil_Db_Delete_Arguements} data - TypeORM 的查询选项。
 * @param {Function} data.findOne - 一个函数，用于根据 ID 查找记录。
 * @param {number} data.id - 需要删除的记录的唯一标识符。
 * @param {Repository} data.repository - 数据仓库，提供数据删除的方法。
 * @returns {Promise<'删除成功'>}
 */
export async function util_Db_Delete({
  findOne,
  id,
  repository,
}: IUtil_Db_Delete_Arguements): Promise<'删除成功'> {
  const item = await findOne(id);
  if (!item) throw new Class_HttpException_Base('不存在该记录');
  await repository.delete(id);
  return '删除成功';
}

/**
 * @description 通用的查询方法，返回实体的 `id` 和指定的 `name` 字段。
 * @example
 * return await util_Db_FindAllSelect({
 *   repository: this.exampleRepository,
 *   entityAlias: 'example',
 *   name: 'name',
 * });
 * @param obj - 包含查询参数的对象。
 * @param  {Repository<T>} obj.repository - 实体的 TypeORM 存储库。
 * @param  {string} obj.entityAlias - 实体在查询中的别名。
 * @param  {string} obj.name - 要查询的字段名，将作为 `name` 返回。
 * @param  {Record<string, unknown>} [obj.query] - 其他查询参数。
 * @returns 返回满足条件的实体列表，每个实体包含 `id` 和 `name` 字段。
 */
export async function util_Db_FindAllSelect<T>(obj: {
  repository: Repository<T>;
  entityAlias: string;
  name: string;
  query?: Record<string, unknown>;
}): Promise<{ id: number; name: string }[]> {
  const queryBuilder = obj.repository.createQueryBuilder(obj.entityAlias);
  const selectArray = [`${obj.entityAlias}.id AS id`, `${obj.entityAlias}.${obj.name} AS name`];
  return await util_Db_AddAnyWhere({
    queryBuilder: queryBuilder.select(selectArray),
    obj: obj.query,
  }).getRawMany();
}
